%!TEX program = xelatex
% \documentclass[12pt,a4paper]{}
\documentclass[12pt,a4paper]{ctexart}
\usepackage{ctex}
% \usepackage[level]{datetime}
\usepackage[]{datetime}
\newdateformat{ukdate}{\monthname[\THEMONTH] \ordinaldate{\THEDAY}, \THEYEAR}
\newdateformat{cndate}{\THEYEAR 年 \THEMONTH 月 \THEDAY 日}
\usepackage{array}
\usepackage{enumerate}
\usepackage{tikz}
\usepackage{lscape}
\usepackage{pgf}
\usepackage{fontspec}
\usepackage{circuitikz}
\usepackage{multirow}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{graphicx} 
\usepackage{subfig}
% \usepackage{subfigure}
\usepackage{amssymb} % for more unusual symbols
\usepackage{ulem}
\usepackage{bm}
\usepackage{lastpage}
\usepackage{cancel}
\usepackage{fancyhdr}
\usepackage{geometry}
\usepackage[hidelinks]{hyperref}
\usepackage{amsmath}
\usepackage{autobreak}
\usepackage{longtable}
\usepackage{enumitem}
\usepackage{url}
\pagestyle{plain} % plain=standard fancy=custom [fancyhdr]
% \pagestyle{fancy} % plain=standard fancy=custom [fancyhdr]
\geometry{left=3.17cm, right=3.17cm, top=2.54cm, bottom=2.54cm} % 页边距 [geometry]
% \geometry{left=2.07cm, right=2.07cm, top=2.04cm, bottom=2.04cm} % 页边距 [geometry]
\tikzstyle{block} = [draw, rectangle]
\CTEXsetup[format={\Large\bfseries}]{section}
\def\mathbi#1{\textbf{\em #1}}
\tikzset{
    state/.style = {rectangle, draw=black, text centered, text width=3cm},
}
\allowdisplaybreaks[4]
% \newcommand{\tt}{\fontspec{\ttfamily}}
\newcommand{\mytitle}{\bf 面向对象技术(Java)大作业二\ 说明文档}
% \setcounter{tocdepth}{2} % 限制目录层数
% \renewcommand\contentsname{\centerline{目\qquad 录}} % 重设目录两字格式
% 一级标题加引导点 start
\usepackage[titles,subfigure]{tocloft}
\renewcommand\cftsecdotsep{\cftdotsep}
\renewcommand\cftsecleader{\cftdotfill{\cftsecdotsep}}
% 一级标题加引导点 end
\newcommand{\mc}[2]{\textcolor{#1}{#2}} % 自定义范围内颜色
\definecolor{L0}{RGB}{0,0,0}
\definecolor{L1}{RGB}{210,2,0}
\definecolor{L2}{RGB}{245,197,3}
\definecolor{L3}{RGB}{249,153,53}
\definecolor{L4}{RGB}{0,160,234}
\definecolor{L5}{RGB}{37,172,116}
\definecolor{L6}{RGB}{139,9,135}
\definecolor{L7}{RGB}{215,160,80}
\definecolor{L8}{RGB}{229,223,135}
\definecolor{L9}{RGB}{130,140,71}
\definecolor{L10}{RGB}{186,101,81}
\definecolor{L12}{RGB}{25,99,164}
\definecolor{L14}{RGB}{178,137,195}
\definecolor{L17}{RGB}{12,68,85}
\definecolor{S1}{RGB}{234,4,55}
\definecolor{S2}{RGB}{135,211,0}
\definecolor{S3}{RGB}{255,209,0}
\definecolor{S4}{RGB}{79,31,145}
\definecolor{S5}{RGB}{162,76,200}
\definecolor{S6}{RGB}{215,22,113}
\definecolor{S7}{RGB}{255,114,0}
\definecolor{S8}{RGB}{0,158,219}
\definecolor{S9}{RGB}{120,199,235}
\definecolor{S10}{RGB}{188,135,230}
\definecolor{S11}{RGB}{124,34,48}
\definecolor{S12}{RGB}{0,123,99}
\definecolor{S13}{RGB}{242,147,209}
\definecolor{S14}{RGB}{127,120,0}
\definecolor{S15}{RGB}{187,167,134}
\definecolor{S16}{RGB}{50,212,203}
\definecolor{S17}{RGB}{182,119,112}
\definecolor{S18}{RGB}{214,164,97}

% lstlisting style start
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{0.95,0.95,0.92}

\lstdefinestyle{mystyle}{
    backgroundcolor=\color{backcolour},   
    commentstyle=\color{codegreen},
    keywordstyle=\color{magenta},
    numberstyle=\small\color{codegray},
    stringstyle=\color{codepurple},
    basicstyle=\ttfamily\footnotesize,
    breakatwhitespace=false,         
    breaklines=true,                 
    captionpos=b,                    
    keepspaces=true,                 
    numbers=left,                    
    numbersep=5pt,                  
    showspaces=false,                
    showstringspaces=false,
    showtabs=false,                  
    tabsize=4
}

\lstset{style=mystyle}
% lstlisting style end

\hypersetup{
	colorlinks=true,
	linkcolor=blue,
	filecolor=blue,      
	urlcolor=red,
	citecolor=green,
}

% \input{preamble}
% \setlength{\topmargin}{-0.5in}
% \setlength{\textheight}{9in}
% \setlength{\oddsidemargin}{0in}
% \setlength{\evensidemargin}{0in}
% \setlength{\textwidth}{6.5in}

\newcommand{\instbit}[1]{\mbox{\scriptsize #1}}
\newcommand{\instbitrange}[2]{~\instbit{#1} \hfill \instbit{#2}~}

\newcommand{\upcite}[1]{$^{\cite{#1}}$}
\newcommand{\inlineCode}[1]{\texttt{\textcolor{L3}{#1}}}
\newcommand{\inc}[1]{\inlineCode{#1}}
\newcommand{\tg}[1]{\texttt{#1}}
\renewcommand{\figureautorefname}{图}
% \renewcommand{\subfloatautorefname}{图}
\newcommand{\fref}[1]{图 \ref{#1} }
\renewcommand{\tableautorefname}{表}
\newcommand{\aref}[1]{ \autoref{#1} }
\newcommand{\sref}[1]{第 \ref{#1} 节}

% content format start
% \usepackage{zhnumber}
% \renewcommand{\thesection}{\zhnum{section}}
% \renewcommand{\thesubsection}{\rmfamily{\arabic{section}.\arabic{subsection}}}
% content format end

% fancy style start
% \lhead{}
% \chead{}
% \chead{\textsl{\zihao{-5}\mytitle 使用说明书}}
% \rhead{}
% \rhead{\textsl{\zihao{-5}{第 \thepage 页}}\ \ \ \ \ \ }
% \lfoot{}
% \cfoot{\thepage\ / \pageref{lastpage}}
% \rfoot{}
% fancy style end
\begin{document}

\title{\mytitle}
\author{FM2\_15577\\狄农雨\ 320200930791\\王小溪\ 320200931330} 
\date{\cndate\today} 
% \thispagestyle{myheadings}
\maketitle 

% \ \newline
% \newline
% \newline
% \newline
% \newline
% \begin{center}
%     \zihao{1} \songti  \mytitle \\
%     \zihao{1} \songti （使用说明书）\\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \ \\
%     \zihao{4} \songti 二〇二一年八月
% \end{center}
% \thispagestyle{empty}
% \newpage
% \setcounter{page}{1}

% \ \newline
% \newline
% \newline
\tableofcontents
\newpage


% \renewcommand{\vec}{\overrightarrow}

\section{成员及分工}

本次作业中，我们的小组名为\mc{L4}{\textbf{15577}}（项目名称：\mc{L4}{\textbf{FM2\_15577}}），由两名成员组成：狄农雨（320200930791）、王小溪（320200931330），成员分工见\autoref{table:divisionOfLabor}。

\begin{table}[htbp]
    \centering
    \caption{成员分工表}
    \label{table:divisionOfLabor}
    \begin{tabular}{|p{.15\textwidth}<{\centering}|p{.75\textwidth}<{\centering}|}\hline
        成员 & 主要分工 \\ \hline
        狄农雨 & 后端服务器环境准备和数据库搭建，客户端与服务器间网络通信及接口设计，服务器端多线程响应客户端请求等 \\ \hline
        王小溪 & 前端框架搭建，数据有效性验证等 \\ \hline
    \end{tabular}
\end{table}

\section{设计需求}
\label{requirementSection}
根据场景概述、管理规则和具体要求等内容，本部分将系统设计的需求按重要性递减顺序分为功能需求、逻辑需求和其他需求三方面进行梳理。

\subsection{功能需求}
\label{requirementSectionFunction}
\begin{enumerate}
    \item 用户能够正常开户/销户；
    \item 用户能够正确查询余额；
    \item 用户能够正确存钱/取钱；
    \item 用户能够实现正确转账功能；
    \item 用户能够修改密码和其它信息(除 id 和身份证(学号))；
    \item 银行管理员可以通过导入 xls 批量开户；
    \item 银行管理员可以生成一个 pdf 版综述报告，报告内容包括但不限于目前账户总数，今年新开户数，总余额等数据；
    \item 可以将特定查询信息（用户信息）导出为 xls 文件。
\end{enumerate}

\subsection{逻辑需求}
\label{requirementSectionLogical}
\begin{enumerate}
    \item 年满18岁才可以申请开户；
    \item 年满70岁的老人强制销户；
    \item 账户余额必须大于等于0，包括取钱后要保证余额大于等于0、转账后要保证余额大于等于0；
    \item 用户名长度不超过 10 个汉字长度；
    \item 密码长度不少于 4 位；
    \item 身份 ID 为 12 位数字；
    \item 手机号为 11 位数字；
    \item 性别用一位字符(M:男，F:女)；
    \item 日期需要是合法日期；
    \item 多个用户不能同时修改数据表项。
\end{enumerate}

\subsection{其他需求}
\label{requirementSectionOther}
\begin{enumerate}
    \item 本项目需要作为带有GUI界面的Java应用程序实现；
    \item 采用云计算模式，在openEuler操作系统上完成；
    \item 使用面向对象的设计：具有类，并使用实例和方法；
    \item 使用网络理论、数据库技术、GUI 技术、多线程技术等理论和技术开发一套微系统用来完成这些服务；
    \item 项目实现过程中，需要考虑到用户的信息安全，对关键信息采取适当的保护措施。
\end{enumerate}

\section{设计方案}
\subsection{粒度分析}
\label{liduAnalysis}
\subsubsection{客户端}
根据\sref{requirementSection}梳理的需求描述，项目需要使用面向对象的设计。本节将对需求进行具体分析，完成类的抽象设计。

首先，考虑\sref{requirementSectionFunction}提到的功能需求：第1、6、7、8条涉及管理系统层面；第2、3、4、5条涉及用户层面。

然后，考虑\sref{requirementSectionLogical}提到的逻辑需求：第1条需要管理系统在用户开户时对用户年龄进行检验；第2条需要管理系统在日常维护时对现有用户年龄进行校验，并具备为年满70岁的用户销户的功能；第3条需要在数据库表、用户进行取款、用户进行转账时对账户余额进行检验；第4、5、6、7、8、9条需要管理系统在用户开户时对信息进行检验；第10条需要管理系统在修改数据库表项数据时合理使用同步锁。

之后，考虑\sref{requirementSectionOther}提到的其他需求。分析可知，该部分仅涉及代码实现细节，故此处不展开分析。

综上，按涉及到的类的抽象粒度由细到粗排序，系统中涉及到的粒度最细的类为用户（Account），其次是管理系统（BankSystem），最后是整个系统封装而成的主类（BankSystemGUI）。其关系为：用户（Account）行为由管理系统（BankSystem）监管，管理系统（BankSystem）为用户（Account）提供服务支持；管理系统（BankSystem）由主类（BankSystemGUI）调用启动；主类（BankSystemGUI）同时负责GUI界面的控制。

需要注意的是，柜台工作人员在登录系统后，在管理系统（BankSystem）层面进行业务活动，故不单独设置工作人员类。

为使客户端实现与服务器端的通信，在客户端需要加入与服务器端口监听程序（Server，见\sref{section:serverLiduAnalysis}）对应的类：客户端请求程序（Client），负责提供客户端向服务器端发出请求的方法接口。

故该系统客户端中存在三个不同的类，粒度由粗到细依次为：主类（BankSystemGUI）、管理系统（BankSystem）、用户（Account）和客户端请求程序（Client）。

\subsubsection{服务器端}
\label{section:serverLiduAnalysis}
在服务器端，需要重点考虑的是后端的业务需要。由于我们采用的是C/S模式，即客户端/服务器端程序模式（在服务端实现多线程，每一个客户端申请连接，就开一个线程提供服务，在服务端访问数据库服务，客户端看不到数据库，并且此模式下要主要对数据库访问时使用同步锁机制），服务器端需要具备服务器端口监听程序、多线程的请求处理和数据库连接与查询执行功能。故我们对服务器端业务流程进行梳理，得到三个不同的类：服务器端口监听程序（Server）、支持多线程的请求处理程序（ServerHandler）和数据库连接器（Connector）。其关系为：服务器端口监听程序（Server）在服务器端常驻运行，在收到客户端连接请求时，对每个连接请求创建新的线程执行请求处理程序（ServerHandler）。请求处理程序（ServerHandler）在与客户端建立连接后，根据客户端的请求信息，在需要数据库表中数据时调用数据库连接器（Connector）处理数据库相关增删改查操作。

故该系统服务器端中存在三个不同的类，粒度由粗到细依次为：服务器端口监听程序（Server）、支持多线程的请求处理程序（ServerHandler）和数据库连接器（Connector）。

\subsection{接口方法分析}
在确认类的粒度后，具体分析各项功能需求和逻辑需求，对每个类所需完成的任务以及类间所需的方法接口进行分析。

结合\sref{liduAnalysis}的分析，考虑功能需求：
\begin{itemize}
    \item 第1条需要管理系统提供开户/销户方法；
    \item 第2条需要用户具备查询余额方法；
    \item 第3、4条需要用户具备存钱/取钱/转账方法；
    \item 第5条需要用户具备获取和修改用户信息的方法；
    \item 第6条需要管理系统具备读取xls和通过xls批量开户的方法；
    \item 第7条需要管理系统具备导出pdf格式综述报告的方法；
    \item 第8条需要管理系统具备导出xls格式账户信息的方法。
\end{itemize}

其次，考虑逻辑需求：
\begin{itemize}
    \item 第1、4、5、6、7、8、9条需要管理系统在开户时，检查用户提供的信息是否符合要求；
    \item 第2条需要管理系统在日常维护时，检查用户的年龄（出生日期）是否符合要求；
    \item 第3条需要数据库表存储时检查账户余额是否符合要求，需要用户取钱/转账操作时检查余额是否符合要求；
    \item 第10条需要数据库连接器在执行查询操作时，合理设置同步锁。
\end{itemize}

综合上述分析和业务逻辑，所有类的属性及方法的具体设计如下：

\subsubsection{客户端请求程序 Client}

客户端请求程序类负责处理所有客户端向服务器端发出的请求，并处理服务器返回的数据信息。

客户端请求程序（Client）类不包含任何属性。

客户端请求程序（Client）类的方法包含：
\begin{itemize}
    \item \tg{SHA256()}：求SHA256杂凑值。以表示原始密码的String类值为参数，求其对应的SHA256杂凑值（哈希值），用于对密码信息的加密保护；
    \item \tg{send()}：发送请求，接收并解析服务器发回的回复包。以JSONObject类值为参数，该值包含若干键值对，其中包含请求类型和附加信息，返回值为接收到的回复信息，以JSONObject类值返回。通信过程采用字符形式通信。
\end{itemize}

\subsubsection{用户 Account}

考虑到账户余额等实时信息存储在客户端时需要定期更新，会对服务器端造成一定的负担，故采用懒处理方式，在需要使用信息时向服务器发起请求，获取相应信息。故用户（Account）类只存储账户ID和静态的Client实例。用户（Account）类的所有涉及修改的操作均需与服务器进行通信（利用\texttt{Client.send()}方法向服务器发送请求），在数据库表中同步完成数据更新，若不能正常完成数据修改，由数据库连接器抛出\verb|SQLException|异常。考虑到用户的出生日期不会发生改变，更新信息时不允许更新用户ID、身份ID、出生日期、余额（仅可通过合法的存款、取款、转账操作修改），其他信息可正常修改。

用户（Account）类的属性包含：账户ID和静态的Client实例。其中，Client用于实现与服务器的通信及密码加密保护。

用户（Account）类的方法包含：
\begin{itemize}
    \item \verb|getAid()|：获取账户ID的值；
    \item \verb|getBalance()|：查询余额；
    \item \verb|deposit()|：存钱。需要以存款金额为参数；
    \item \verb|withdraw()|：取钱。需要以取款金额为参数，取款时检查是否符合余额非负限制（由数据库限制实现）；
    \item \verb|transfer()|：转账。需要以转账对象账户ID和转账金额为参数，转账时检查转账对象账户ID是否存在（由ServerHandler处理）以及是否符合余额非负限制（由数据库限制实现）；
    \item \verb|updateInfo()|：更新用户名、密码、手机号和性别。需要以新的各项值为参数，当存在新用户名长度不符合要求（超过10）、新密码长度不符合要求时（小于4）和新手机号长度不符合要求（不等于11）任一情况时，抛出异常；
    \item \verb|Account()|：类的构造方法。以用户ID为参数，由管理系统在登录成功时使用该构造函数，创建用户类实例。
\end{itemize}

\subsubsection{管理系统 BankSystem}

在客户端提供服务的类。用户在管理系统中登录后，主要在登录用户层面进行业务操作；柜台工作人员在管理系统中登录后，主要在管理系统层面进行业务操作。故在类的设计上以属性值进行区分，展示相应GUI并使用相应实例的方法完成业务流程。

管理系统（BankSystem）类的属性包含：当前登录的用户（Account类），当前登录的工作人员用户名和静态的Client实例。其中，Client用于实现与服务器的通信及密码加密保护。

管理系统（BankSystem）类的方法包含：
\begin{itemize}
    \item \verb|getAccount()|：获取当前登录的用户账户ID（调用登录用户的\verb|getAid()|方法实现）；
    \item \verb|getAdmin()|：获取当前登录的管理员账户名（获取属性值）；
    \item \verb|checkAndConvertAid()|：检验账户ID（String类）的有效性（10位数字），并转换为接口实际使用的类型（long类型）；
    \item \verb|checkAndConvertAdminName()|：检验管理员账户名（String类）的有效性（长度不超过20），并转换为接口实际使用的类型（String类）；
    \item \verb|checkAndConvertName()|：检验姓名（String类）的有效性（长度不超过10），并转换为接口实际使用的类型（String类）；
    \item \verb|checkAndConvertCipher()|：检验密码（String类）的有效性（长度不小于4），并转换为接口实际使用的类型（String类）；
    \item \verb|checkAndConvertID()|：检验身份证号（String类）的有效性（12位数字），并转换为接口实际使用的类型（long类型）；
    \item \verb|checkAndConvertPhone()|：检验手机号（String类）的有效性（11位数字），并转换为接口实际使用的类型（long类型）；
    \item \verb|checkAndConvertSex()|：检验性别（String类）的有效性（1个字符，M或F），并转换为接口实际使用的类型（char类型）；
    \item \verb|checkAndConvertDob()|：检验出生日期（String类）的有效性（\tg{YYYY-MM-DD}格式，年龄在18-70岁之间），并转换为接口实际使用的类型（LocalDate类）；
    \item \verb|checkAndConvertAmount()|：检验金额（String类）的有效性（不超过两位小数实数格式），并转换为接口实际使用的类型（String类）；
    \item \tg{registerAccount()}：开户。需要以用户名、密码、身份ID、手机号、性别、出生日期为参数，若信息不能通过数据有效性验证，则抛出异常；
    \item \tg{loginAccount()}：登录用户。需要以账户ID、密码为参数，若信息不能通过数据有效性验证或登录失败，则抛出异常；
    \item \tg{removeAccount()}：销户。在登录用户为普通用户时可用，仅在数据库中修改销户标识，不删除记录，且不提供销户后重新恢复账户方法，防止不符合正常账户使用逻辑的行为发生；
    \item \tg{registerAdminAccount()}：注册管理员账户，当登录账户为管理员账户时可用。需要以用户名、密码为参数，若信息不能通过数据有效性验证，则抛出异常；
    \item \tg{loginAdminAccount()}：登录管理员账户。需要以用户名、密码为参数，若信息不能通过数据有效性验证或登录失败，则抛出异常；
    \item \verb|getBalance()|：查询余额（调用登录用户的\verb|getBalance()|方法实现）；
    \item \verb|deposit()|：存钱（调用登录用户的\verb|deposit()|方法实现）。需要以存款金额为参数；
    \item \verb|withdraw()|：取钱（调用登录用户的\verb|withdraw()|方法实现）。需要以取款金额为参数，取款时检查是否符合余额非负限制（由数据库限制实现）；
    \item \verb|transfer()|：转账（调用登录用户的\verb|transfer()|方法实现）。需要以转账对象账户ID和转账金额为参数，转账时检查转账对象账户ID是否存在（由ServerHandler处理）以及是否符合余额非负限制（由数据库限制实现）；
    \item \verb|updateInfo()|：更新用户名、密码、手机号和性别（调用登录用户的\verb|updateInfo()|方法实现）。需要以新的各项值为参数，当存在新用户名长度不符合要求（超过10）、新密码长度不符合要求时（小于4）和新手机号长度不符合要求（不等于11）任一情况时，抛出异常；
    \item \tg{registerAccountFromXls()}：从xls文件批量开户（限管理员使用）。该方法主要负责读取xls文件内容，然后对每个新账户调用\tg{registerAccount()}方法完成开户。返回一个String，包含开户成功和失败的数量，并在xls文件相同目录下创建一个\path{<filename>-res.xls}文件作为操作结果文件，其中包含每个账户创建的结果以及原因信息；
    \item \tg{exportAccountInfo()}：导出xls格式用户信息表（限管理员使用）。以导出目录名为参数；
    \item \tg{exportReport()}：导出pdf格式综述报告（限管理员使用）。以导出目录名为参数；
    \item \tg{logout()}：退出登录。
\end{itemize}

\subsubsection{主类 BankSystemGUI}

主类（BankSystemGUI）中包含GUI元素属性和静态的BankSystem实例。其中，BankSystem用于提供操作接口。

主类（BankSystemGUI）中包含若干私有GUI页面方法和一个公有静态方法\tg{main()}，其作用是作为整个系统启动运行的接口，并提供GUI界面支持。

\subsubsection{服务器端口监听程序 Server}

服务器端口监听程序（Server）在服务器端常驻运行，用于处理客户端发起的网络连接请求。

服务器端口监听程序（Server）类不包含任何属性。

服务器端口监听程序（Server）类的方法只包含主函数。当有客户端的网络连接请求时，创建新的进程，运行请求处理程序（ServerHandler），根据客户端请求内容进行响应。监听端口为10007。

\subsubsection{请求处理程序 ServerHandler}

请求处理程序（ServerHandler）类包含私有属性：需要处理的网络连接（Socket）、需要处理的请求（JSONObject）、处理请求的返回值（JSONObject）和数据库连接器（Connector）。

请求处理程序（ServerHandler）类的方法包含：
\begin{itemize}
    \item \tg{ServerHandler()}：构造函数。以需要处理的Socket为参数；
    \item \tg{processRegisterAccount()}：处理用户注册的方法。遇到异常抛出；
    \item \tg{processLoginAccount()}：处理用户登录的方法。遇到异常抛出；
    \item \tg{processRemoveAccount()}：处理用户销户的方法。遇到异常抛出；
    \item \tg{processRegisterAdminAccount()}：处理管理员账户注册的方法。遇到异常抛出；
    \item \tg{processLoginAdminAccount()}：处理管理员账户登录的方法。遇到异常抛出；
    \item \tg{processGetBalance()}：处理用户获取余额的方法。遇到异常抛出；
    \item \tg{processDeposit()}：处理用户存款的方法。遇到异常抛出；
    \item \tg{processWithdraw()}：处理用户取款的方法。遇到异常抛出；
    \item \tg{processTransfer()}：处理用户转账的方法。遇到异常抛出；
    \item \tg{processUpdateInfo()}：处理用户更新信息的方法。遇到异常抛出；
    \item \tg{processExportAccountInfo()}：处理系统导出用户信息表的方法。遇到异常抛出；
    \item \tg{processExportReport()}：处理系统导出年度报表的方法。遇到异常抛出；
    \item \tg{run()}：运行函数。由于请求处理程序是可执行（Runnable）类的实现，故设置运行函数。
\end{itemize}

\subsubsection{数据库连接器 Connector}

数据库连接器（Connector）类包含三个私有属性：到数据库的连接（Connection）、数据库执行环境（Statement）和查询结果（ResultSet）。

数据库连接器（Connector）类的方法包含：
\begin{itemize}
    \item \tg{start()}：开始连接函数。执行该函数以建立到数据库的连接；
    \item \tg{execute()}：查询执行函数。以需要执行的查询为参数，结果会存储到私有属性Resultset；
    \item \tg{melock()}：用户表上同步锁；
    \item \tg{melockadmin()}：管理员表上同步锁；
    \item \tg{meunlock()}：解除同步锁；
    \item \tg{getResult()}：获取查询结果函数；
    \item \tg{close()}：关闭连接器。
\end{itemize}

\section{具体实现}

\subsection{GUI流程图}
\label{section:flowDiagram}

根据系统功能和GUI实现，构建出系统的GUI流程图如\fref{fig:flowDiagram}：

\begin{figure}[ht]
    \centering
    \includegraphics[width=.9\textwidth]{40011.png}
    \caption{流程图（部分细节未体现）}
    \label{fig:flowDiagram}
\end{figure}

% \subsection{实现细节}
% \mc{L1}{TBD}
% \label{section:details}
% 在\sref{section:flowDiagram}中展示了整个系统的大致流程图，未体现函数调用的实现细节。本节将对各函数调用的实现细节展开介绍。

% 各项调用的细节如下：
% \begin{itemize}
%     \item \tg{ManageSystem.init()}：对管理系统初始化，并载入预加载数据。
%     \item \tg{ManageSystem.menu()}：管理系统主菜单方法。以do-while结构循环实现菜单调用。输入选项不合法时，提示选项不合法原因并返回主菜单。
%     \item \tg{ManageSystem.Satellites.listAll()}：卫星列表显示所有卫星信息的方法。该方法遍历列表内所有卫星，并将所有卫星的信息以表格形式（字符界面）输出（使用 \verb|Satellites.listWhere()| 实现）。
%     \item \tg{ManageSystem.Satellites.add()}：卫星列表添加卫星的方法，参数为卫星的COSPARID、名称、轨道半径（默认添加的卫星处于激活状态）。首先，该方法创建一个卫星（Satellite）类的实例。若创建失败，则提示添加失败，直接返回。否则，检查卫星轨道间距不小于0.2，COSPARID唯一。若检验通过，将创建好的实例加入到卫星列表中，并提示添加成功和创建的卫星信息。否则，提示添加失败（卫星轨道间距不合法或COSPARID不唯一），直接返回。
%     \item \tg{ManageSystem.Satellites.remove()}：卫星列表删除卫星的方法，参数为卫星的COSPARID。首先，在卫星列表中查找目标卫星。若该卫星不存在，则提示删除失败（卫星不存在），直接返回。否则，将目标卫星从卫星列表中删除，并提示删除成功和删除的卫星信息。
%     \item \tg{ManageSystem.Satellites.activateSatellite()}：卫星列表激活已封锁卫星的方法，参数为卫星的COSPARID。首先，在卫星列表中查找目标卫星。若该卫星不存在，则提示激活失败（卫星不存在），直接返回。否则，调用目标卫星的\tg{setActivated()}方法，参数为\tg{true}，并提示激活成功和激活的卫星信息。
%     \item \tg{ManageSystem.Satellites.blockSatellite()}：卫星列表封锁卫星的方法，参数为卫星的COSPARID。首先，在卫星列表中查找目标卫星。若该卫星不存在，则提示封锁失败（卫星不存在），直接返回。否则，调用目标卫星的\tg{setActivated()}方法，参数为\tg{false}，并提示封锁成功和封锁的卫星信息。
%     \item \tg{ManageSystem.Satellites.listBlocked()}：卫星列表显示所有已封锁卫星信息的方法。该方法遍历列表内所有卫星，并将所有可用状态为\tg{false}（封锁状态）的卫星信息以表格形式（字符界面）输出（使用 \tg{Satellites.list\\Where()} 实现）。
%     \item \tg{ManageSystem.Satellites.findByName()}：卫星列表按名称模糊查找卫星的方法，参数为一个字符串，表示要查找的名称。该方法遍历列表内所有卫星，并将所有名称中含有查找内容的卫星信息以表格形式（字符界面）输出（使用 \verb|Satellites.listWhere()| 实现）。
%     \item \tg{ManageSystem.Satellites.updateInfo()}：卫星列表更新现有卫星信息的方法，参数为卫星的COSPARID。首先，在卫星列表中查找目标卫星。若该卫星不存在，则提示修改失败（卫星不存在），直接返回。否则，调用目标卫星的对应set方法。若修改时通过逻辑检验，则提示修改成功和修改后的卫星信息。否则，提示修改失败（数据不合法），直接返回。
%     \item \tg{System.exit(0)}：退出应用程序。
% \end{itemize}

\subsection{复杂度分析}

除去与数据库相关的增删改查操作外，系统中其他操作的复杂度均不超过线性（$\mathcal{O}(n)$），不构成性能瓶颈，故不作进一步分析与优化。

\subsection{安全加固}

考虑到服务器开放端口可能会收到系统外部的非法流量干扰，我们对客户端请求在满足json格式的基础上，加入vk字段。其中，客户端到服务器端请求中该字段值为\verb|Fei_mA$Bank|的SHA256值；服务器端到客户端请求中该字段值为\verb|fEi$Ma&baNk|的SHA256值。服务器端和客户端在收到请求/回复数据包时先对该字段进行校验，通过后再进行相应处理，以避免系统受外部非法网络数据包攻击而被破坏。

\section{环境准备}

\subsection{Java环境}
准备一台openEuler操作系统的云服务器，此处选择华为云openEuler操作系统云服务器，操作系统版本为openEuler 20.03，其他配置信息见\fref{fig:serverInfo}：

\begin{figure}[ht]
    \centering
    \includegraphics[width=.9\textwidth]{40001.png}
    \caption{云服务器配置信息}
    \label{fig:serverInfo}
\end{figure}

购买完成后，通过ssh登录到云服务器。

配置免密认证的方式见前次实验的说明文档。

% 首先，配置ssh免密认证，在云服务器上执行\inc{ssh-keygen}命令，生成服务器ssh公私钥对。然后，将本地计算机系统用户目录下\path{.ssh/id_rsa.pub}公钥文件的内容复制并粘贴到云服务器端\path{~/.ssh/authorized_keys}文件中，即可完成ssh免密认证配置（\fref{fig:sshConfig}、\fref{fig:sshConfigAfter}）。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.4\textwidth]{40002.jpg}
%     \caption{配置ssh免密认证}
%     \label{fig:sshConfig}
% \end{figure}

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.4\textwidth]{40003.jpg}
%     \caption{完成ssh免密认证配置}
%     \label{fig:sshConfigAfter}
% \end{figure}

完成ssh免密认证配置后，从本地登录到云服务器无需密码，使用\tg{scp}命令双向传输文件亦无需密码。

之后，在本地\path{hosts}文件中加入以下内容：

\begin{lstlisting}
    114.115.144.46	node901
\end{lstlisting}

即可用\inc{ssh root@node901}命令替代\inc{ssh root@114.115.144.46}登录云服务器。使用\tg{scp}命令双向传输文件时亦可。

最后，执行\inc{yum install -y java-1.8.0-openjdk*}安装Java JDK 8版本环境，安装完成后执行\inc{java -version}指令验证环境能够正常运行（\fref{fig:javaInstalled}）。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.66\textwidth]{40004.png}
    \caption{Java JDK 8版本环境安装完成}
    \label{fig:javaInstalled}
\end{figure}

% \subsection{JSP环境}
% 执行以下指令创建\tg{tomcat}账户，并在该非root账户上运行Tomcat服务。

% \begin{lstlisting}[language=sh]
%     sudo groupadd tomcat
%     sudo useradd -M -s /bin/false -g tomcat -d /opt/tomcat tomcat
% \end{lstlisting}

% 执行以下指令下载并解压Tomcat压缩包：

% \begin{lstlisting}
%     cd ~
%     wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.76/bin/apache-tomcat-9.0.76.tar.gz
%     tar -zxvf apache-tomcat-9.0.76.tar.gz
%     mv apache-tomcat-9.0.76 /opt/
%     cd /opt/apache-tomcat-9.0.76
%     chmod 777 ./bin/startup.sh
%     chmod 777 ./bin/shutdown.sh
% \end{lstlisting}

% 修改配置文件\path{~/.bashrc}，加入以下内容：

% \begin{lstlisting}
%     export CATALINA_HOME=/opt/apache-tomcat-9.0.76
%     export CATALINA_BASE=/opt/apache-tomcat-9.0.76
%     export PATH=$PATH:$CATALINA_HOME/bin
% \end{lstlisting}

% 完成配置后，执行\inc{startup.sh}即可启动Tomcat服务（\fref{fig:40101}），执行\inc{shutdown.sh}即可关闭Tomcat服务。启动Tomcat服务后，访问\url{node901:8080}检验服务是否正常启动，若如\fref{fig:40102}所示，即正常启动。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{40101.png}
%     \caption{启动Tomcat服务}
%     \label{fig:40101}
% \end{figure}

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{40102.png}
%     \caption{成功启动服务后的页面}
%     \label{fig:40102}
% \end{figure}

\subsection{安装MySQL}
\label{section:installmysql}
首先，执行以下指令卸载MariaDB。

\begin{lstlisting}[language=sh]
    rpm -qa | grep mariadb
    rpm -e --nodeps mariadb-connector-c-3.0.6-6.oe1.x86_64
\end{lstlisting}

执行以下指令创建mysql用户和数据文件夹用于mysql的运行。

\begin{lstlisting}[language=sh]
    groupadd mysql
    useradd -r -g mysql -s /bin/false mysql
    chown -R mysql:mysql /usr/local/mysql
\end{lstlisting}

执行以下指令下载安装MySQL。

\begin{lstlisting}[language=sh]
    yum install -y libaio
    cd /usr/local/
    wget https://dev.mysql.com/get/Downloads/MySQL-8.0/mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz
    tar -xvf mysql-8.0.33-linux-glibc2.12-x86_64.tar.xz
    ln -s mysql-8.0.33-linux-glibc2.12-x86_64 mysql
    cd mysql
    mkdir mysql-files
    chown mysql:mysql mysql-files
    chmod 750 mysql-files
    bin/mysqld --initialize --user=mysql
    # A temporary password is generated for root@localhost: dew,r=8Ao3?%
    bin/mysql_ssl_rsa_setup
    bin/mysqld_safe --user=mysql &
    cp support-files/mysql.server /etc/init.d/mysql.server
    cd /usr/local/
    wget https://dev.mysql.com/get/Downloads/Connector-J/mysql-connector-j-8.0.33.tar.gz
    tar -xvf mysql-connector-j-8.0.33.tar.gz
    ln -s mysql-connector-j-8.0.33 mysql-connector-j
\end{lstlisting}

修改配置文件\path{~/.bashrc}，加入以下内容后执行\inc{source \~{}/.bashrc}使配置生效。

\begin{lstlisting}
    export PATH=$PATH:/usr/local/mysql/bin
    export CLASSPATH=/usr/local/mysql-connector-j/mysql-connector-j-8.0.33.jar:$CLASSPATH
\end{lstlisting}

完成环境变量配置后，执行\inc{mysql --version}查看版本，确认安装成功。

为了数据安全，在\tg{mysql}中执行以下指令修改数据库root账户密码。

\begin{lstlisting}[language=sql]
    mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'Mysql@root$Java2023';
\end{lstlisting}

执行以下指令配置数据库。

\begin{lstlisting}[language=sql]
    CREATE DATABASE bank;
    CREATE USER 'bankAdmin'@'localhost' IDENTIFIED BY 'bank@Java2023';
    GRANT ALL ON bank.* TO 'bankAdmin'@'localhost';
\end{lstlisting}

\subsection{配置MySQL数据库表}

\subsubsection{用户信息表}

根据\sref{section:installmysql}的设置，系统访问数据库时使用的账户为\verb|bankAdmin|，访问的数据库为\verb|bank|。本节将介绍数据库表的创建方式。

根据业务流程对账户信息的相关要求，设计数据库表\verb|AccountInfo|。考虑到数据安全，密码长度校验由系统前端完成，数据库中存储的密码为密码字符串的hash（SHA256，对应\verb|CHAR|长度为64）值。账户ID、身份ID、手机号均以\verb|INT|类型存储，数据校验在系统前端与数据库表中完成。为避免浮点误差造成的余额变化不精确问题，账户余额以\verb|DECIMAL|（定点小数）类型存储，保留两位小数，总位数为64位，可以满足精度要求和大多数使用需求。为防止用户利用同一个身份ID申请多个账户，身份ID列设置\verb|UNIQUE|限制。为防止用户通过销户再开户重复获取赠送福利扰乱经济秩序，增加销户标识（\verb|closed|）列，销户仅修改\verb|closed|列；用户销户后，不得重新开户（亦可防止冒用过世人员身份开户情况）；标识列在登录时自动更新，对于年龄超过70岁的用户会在再次登录时更新标识并提醒被销户；支持删除所有70岁以上账户（管理员账户登录时自动完成标识列更新），同时不允许70岁以上用户开户。同时，为支持报表生成本年度开户数量信息，账户信息表中加入开户年份（\verb|openyear|）列。

综合上述考虑，创建数据库表\verb|AccountInfo|的代码如下（见\path{createTable.sql}）：

\begin{lstlisting}[language=sql]
    CREATE TABLE AccountInfo ( -- 所有属性非空
        aid BIGINT NOT NULL PRIMARY KEY, -- 10位数字采用BIGINT存储，数据范围由CONSTRAINT检验，下同
        name VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, -- 设置字符集为utf8mb4，兼容长度为10的汉字
        cipher CHAR(64) NOT NULL, -- 存储密码的hash（SHA256）值
        id BIGINT NOT NULL UNIQUE, -- 不允许同一身份ID拥有多个账户
        phone BIGINT NOT NULL,
        sex ENUM('F', 'M') NOT NULL, -- 值从两个候选枚举值选取，非空
        dob DATE NOT NULL, 
        balance DECIMAL(64,2) NOT NULL,
        closed BOOLEAN NOT NULL,
        openyear INTEGER NOT NULL, -- 开户年份
        CONSTRAINT balance_positive CHECK (balance >= 0), -- 余额不小于0
        CONSTRAINT aid_digit CHECK (aid >= 1000000000 AND aid < 10000000000),
        CONSTRAINT id_digit CHECK (id >= 100000000000 AND id < 1000000000000),
        CONSTRAINT phone_digit CHECK (phone >= 10000000000 AND phone < 100000000000)
    );
\end{lstlisting}

% Test:
% \begin{lstlisting}[language=sql]
%     DROP TABLE AccountInfo;
    % INSERT INTO AccountInfo VALUES (
    %     1234567890,
    %     '测试长度测试长度为十',
    %     SHA2('abc', 256),
    %     320200930791,
    %     18595422403,
    %     'M',
    %     '2002-04-22',
    %     2000,
    %     FALSE,
    %     2023
    % );
%     INSERT INTO AccountInfo VALUES (
%         1234567891,
%         '测试身份ID相同',
%         SHA2('abc', 256),
%         320200930791,
%         18595422403,
%         'M',
%         '2002-04-22',
%         2000,
%         FALSE
%     );
%     INSERT INTO AccountInfo VALUES (
%         1234567891,
%         '测试长度测试长度为十一',
%         SHA2('abc', 256),
%         320200930791,
%         18595422403,
%         'M',
%         '2002-04-22',
%         2000
%     );
%     INSERT INTO AccountInfo VALUES (
%         1234567891,
%         '测试长度测试长度为十',
%         SHA2('abc', 256),
%         320200930791,
%         18595422403,
%         'S',
%         '2002-04-22',
%         2000
%     );
%     INSERT INTO AccountInfo VALUES (
%         1234567891,
%         '12345678901',
%         SHA2('abc', 256),
%         320200930791,
%         18595422403,
%         'S',
%         '2002-04-22',
%         2000
%     );
%     SELECT * FROM AccountInfo;
%     DELETE FROM AccountInfo WHERE aid = 1234567890;
% \end{lstlisting}

\subsubsection{管理员信息表}

对于管理员账号，只需要存名称和密码。创建数据库表\verb|AdminInfo|的代码如下（见\path{createTable.sql}）：

\begin{lstlisting}[language=sql]
    CREATE TABLE AdminInfo ( -- 所有属性非空
        cid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, -- 表主键
        name VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL UNIQUE, -- 设置字符集为utf8mb4，兼容长度为20的汉字，管理员名称作为用户名且唯一
        cipher CHAR(64) NOT NULL -- 存储密码的hash（SHA256）值
    );
\end{lstlisting}

执行以下代码创建初始管理员账户（见\path{createAdminAccount.sql}）：

\begin{lstlisting}[language=sql]
    INSERT INTO AdminInfo (name, cipher) VALUES ('admin', SHA2('Admin@java', 256)); -- 可以根据需要修改账号和密码
\end{lstlisting}

% TEST:
% \begin{lstlisting}[language=sql]
%     CREATE TABLE AdminInfo ( -- 所有属性非空
%         cid BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, -- 表主键
%         name VARCHAR(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL, -- 设置字符集为utf8mb4，兼容长度为20的汉字
%         cipher CHAR(64) NOT NULL -- 存储密码的hash（SHA256）值
%     );
%     INSERT INTO AdminInfo (name, cipher) VALUES ('admin', SHA2('Admin@java', 256));
%     DROP TABLE AdminInfo;
%     show columns from AdminInfo;
%     select * from AdminInfo;
% \end{lstlisting}

\subsection{其他配置}

\subsubsection{JSON}

为使银行系统能够使用JSON，执行以下指令配置JSON环境：

\begin{lstlisting}
    cd /usr/local
    wget https://repo1.maven.org/maven2/org/json/json/20230227/json-20230227.jar
\end{lstlisting}

修改配置文件\path{~/.bashrc}，加入以下内容后执行\inc{source \~{}/.bashrc}使配置生效。

\begin{lstlisting}
    export CLASSPATH=/usr/local/json-20230227.jar:$CLASSPATH
\end{lstlisting}

\subsubsection{screen}

执行\inc{yum install screen}指令安装screen，为后续服务器端监听程序不间断运行提供条件。

\subsubsection{客户端}

由于系统使用了xls和pdf相关的jar包，需要在eclipse中左侧文件上右键，选择\verb|Build Path -> Configure Build Path|（\autoref{fig:buildpath}），在libraries标签中点击\verb|Add JARs|，添加项目根目录的两个jar包（\autoref{fig:buildpathcomplete}），即可完成配置。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.5\textwidth]{54301.png}
    \caption{构建路径菜单}
    \label{fig:buildpath}
\end{figure}

\begin{figure}[ht]
    \centering
    \includegraphics[width=.5\textwidth]{54302.png}
    \caption{添加后的构建路径}
    \label{fig:buildpathcomplete}
\end{figure}

\section{运行测试}
\subsection{代码文件结构}
% \mc{L1}{TBD}
% 所有代码存储在 ``\path{FM320200930791_}狄农雨'' 目录下。在该目录中，包含四个Java代码文件和一个Java包目录。

% 四个Java代码文件为：

\begin{itemize}
    \item \path{FM2_15577}：项目代码目录
    \begin{itemize}
        \item \path{back_end}：后端代码目录
        \begin{itemize}
            \item \path{Connector.java}
            \item \path{Server.java}
            \item \path{ServerHandler.java}
        \end{itemize}
        \item \path{front_end}：前端代码目录
        \begin{itemize}
            \item \path{Account.java}
            \item \path{BankSystem.java}
            \item \path{BankSystemGUI.java}
            \item \path{Client.java}
        \end{itemize}
    \end{itemize}
    \item \path{org/json/*}：\tg{org.json}包代码，提供\verb|JSONObject|等类支持
    \item \path{sql_script}：创建数据库表和管理员账户的sql脚本，在\verb|bank|数据库下执行
    \begin{itemize}
        \item \path{createAdminAccount.sql}
        \item \path{createTable.sql}
    \end{itemize}
    \item \path{itextpdf.jar}：pdf支持jar包
    \item \path{jxl-2.6.10.jar}：xls支持jar包
    \item \path{list.xls}：批量开户用户信息模板
    \item \path{list-res.xls}：批量开户用户信息模板对应开户生成xls结果
    \item \path{accounts.xls}：导出的数据库用户信息表样例
    \item \path{bank-report.pdf}：导出的包含目前账户总数，今年新开户数，总余额数据的报表样例，报表仅包含关键信息，其他内容可根据银行方需求再行修改
    \item \text{说明文档}\path{.pdf}：导出的包含目前账户总数，今年新开户数，总余额数据的报表样例，报表仅包含关键信息，其他内容可根据银行方需求再行修改
\end{itemize}

% 一个Java包目录为：\path{FM320200930791}，上述四个Java代码文件均属于该包。该目录中还包含四个Java代码分别编译得到的四个\path{.class}文件。

\subsection{代码编译}
% \mc{L1}{TBD}


服务器端：在用户根目录下（代码目录/文件存放在根目录下），执行以下指令，即可完成代码编译（见\autoref{fig:compileCode}）。

\begin{lstlisting}[language=sh]
    javac -encoding UTF-8 -d . -sourcepath . FM2_15577/back_end/*.java
\end{lstlisting}

\begin{figure}[ht]
    \centering
    \includegraphics[width=.9\textwidth]{60001.png}
    \caption{在云服务器上完成代码编译}
    \label{fig:compileCode}
\end{figure}

% \mc{L1}{请注意！}Windows客户端编译时需要执行以下指令：

% \begin{lstlisting}[language=sh]
%     javac -encoding UTF-8 -d . -sourcepath . FM2_15577/front_end/*.java FM2_15577/back_end/*.java
% \end{lstlisting}

客户端：在eclipse中，将整个项目文件夹导入，选择主类（BankSystemGUI），点击运行即可。

\subsection{代码执行}
% \mc{L1}{TBD}

\subsubsection{服务器端}

执行\inc{screen -S serverListener}指令，创建分屏，以保证服务不中断。

在分屏中，在用户根目录下，执行\inc{java FM2\_15577.back\_end.Server}指令即可运行服务器端监听程序（见\autoref{fig:runCode}）。按\verb|ctrl+a|后按\verb|d|，即可从分屏中退出，需要回到分屏时，执行\inc{screen -dr serverListener}即可。需要关闭分屏时，执行\inc{ps -ef}，查看分屏对应pid，执行\inc{kill -9 <pid>}指令即可关闭分屏。

若服务器遇到断电等异常情况，重启服务器后，需要执行\inc{/etc/init.d/mysql.server start}指令启动数据库服务。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.9\textwidth]{60002.png}
    \caption{在云服务器上执行Server}
    \label{fig:runCode}
\end{figure}

\subsubsection{客户端}

在eclipse中，将整个项目文件夹导入，选择主类（BankSystemGUI），点击运行即可。

对各选项的执行测试等见\sref{section:showFunc}。

% 在\path{FM2}目录下，执行\inc{java FM2\_15577.front\_end.Main}指令即可运行客户端程序。

\section{功能展示}
\label{section:showFunc}

运行客户端，得到主界面（见\autoref{fig:70001}）。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.45\textwidth]{70001.png}
    \caption{在云服务器上执行代码}
    \label{fig:70001}
\end{figure}

\subsection{用户操作}

\subsubsection{用户注册}

首先，测试部分异常的输入数据（见\autoref{fig:711A}）。

\begin{figure}[ht]
    \centering
    \subfloat[名称长度超过10]{
        \includegraphics[width=.2\textwidth]{71101.png}
        \label{fig:71101}
    }
    \subfloat[密码长度小于4]{
        \includegraphics[width=.2\textwidth]{71102.png}
        \label{fig:71102}
    }
    \subfloat[ID长度不等于12]{
        \includegraphics[width=.2\textwidth]{71103.png}
        \label{fig:71103}
    }\\
    \subfloat[手机号长度不等于11]{
        \includegraphics[width=.2\textwidth]{71104.png}
        \label{fig:71104}
    }
    \subfloat[日期格式不正确]{
        \includegraphics[width=.2\textwidth]{71105.png}
        \label{fig:71105}
    }
    \subfloat[年龄小于18]{
        \includegraphics[width=.2\textwidth]{71106.png}
        \label{fig:71106}
    }
    \subfloat[年龄大于70]{
        \includegraphics[width=.2\textwidth]{71107.png}
        \label{fig:71107}
    }
    \caption{用户注册输入不合法}
    \label{fig:711A}
\end{figure}

输入合法用户信息（见\fref{fig:71108}）完成注册，进入用户功能界面（见\fref{fig:71109}）。

\begin{figure}[ht]
    \centering
    \subfloat[合法用户信息]{
        \includegraphics[width=.2\textwidth]{71108.png}
        \label{fig:71108}
    }
    \subfloat[功能界面]{
        \includegraphics[width=.2\textwidth]{71109.png}
        \label{fig:71109}
    }
    \caption{用户注册}
    % \label{fig:711B}
\end{figure}

点击return即可退出登录。

\subsubsection{用户登录}

测试异常输入数据（见\autoref{fig:712A}）。

\begin{figure}[ht]
    \centering
    \subfloat[密码长度小于4]{
        \includegraphics[width=.2\textwidth]{71201.png}
        \label{fig:71201}
    }
    \subfloat[密码错误]{
        \includegraphics[width=.2\textwidth]{71202.png}
        \label{fig:71202}
    }
    \caption{用户登录输入不合法}
    \label{fig:712A}
\end{figure}

输入正确用户信息（见\fref{fig:71203}）完成登录，进入用户功能界面（见\fref{fig:71204}）。

\begin{figure}[ht]
    \centering
    \subfloat[正确用户信息]{
        \includegraphics[width=.2\textwidth]{71203.png}
        \label{fig:71203}
    }
    \subfloat[功能界面]{
        \includegraphics[width=.2\textwidth]{71204.png}
        \label{fig:71204}
    }
    \caption{用户登录}
    % \label{fig:711B}
\end{figure}

\subsubsection{用户功能测试}

测试各项功能运行情况（见\autoref{fig:713A}）。

\begin{figure}[ht]
    \centering
    \subfloat[余额查询]{
        \includegraphics[width=.2\textwidth]{71301.png}
        \label{fig:71301}
    }
    \subfloat[修改信息（校验同注册，不再展示）]{
        \includegraphics[width=.2\textwidth]{71302.png}
        \label{fig:71302}
    }
    \subfloat[存款]{
        \includegraphics[width=.2\textwidth]{71303.png}
        \label{fig:71303}
    }\\
    \subfloat[取款]{
        \includegraphics[width=.2\textwidth]{71304.png}
        \label{fig:71304}
    }
    \subfloat[余额为负]{
        \includegraphics[width=.2\textwidth]{71305.png}
        \label{fig:71305}
    }
    \subfloat[转账]{
        \includegraphics[width=.2\textwidth]{71306.png}
        \label{fig:71306}
    }
    \subfloat[销户（点击是即可销户）]{
        \includegraphics[width=.2\textwidth]{71307.png}
        \label{fig:71307}
    }
    \caption{用户功能测试}
    \label{fig:713A}
\end{figure}

\subsection{管理员操作}

\subsubsection{管理员登录}

利用初始管理员账户进行登录（见\autoref{fig:720A}）。

\begin{figure}[ht]
    \centering
    \subfloat[密码错误]{
        \includegraphics[width=.2\textwidth]{72001.png}
        \label{fig:72001}
    }
    \subfloat[登录成功]{
        \includegraphics[width=.2\textwidth]{72002.png}
        \label{fig:72002}
    }
    \subfloat[管理员功能界面]{
        \includegraphics[width=.2\textwidth]{72003.png}
        \label{fig:72003}
    }
    \caption{管理员登录}
    \label{fig:720A}
\end{figure}

\subsubsection{管理员注册}

在功能界面注册管理员\verb|admin2|进行测试（见\autoref{fig:721A}）。

\begin{figure}[ht]
    \centering
    \subfloat[密码错误]{
        \includegraphics[width=.2\textwidth]{72011.png}
        \label{fig:72011}
    }
    \subfloat[管理员功能界面]{
        \includegraphics[width=.2\textwidth]{72012.png}
        \label{fig:72012}
    }
    \caption{管理员注册}
    \label{fig:721A}
\end{figure}

\subsubsection{导出报告}

首先，点击\verb|Create report|进入导出报告界面（见\autoref{fig:72021}）。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.3\textwidth]{72021.png}
    \caption{进入导出报告界面}
    \label{fig:72021}
\end{figure}

点击\verb|Browse|打开选择路径界面（见\autoref{fig:72022}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72022.png}
    \caption{打开选择路径界面}
    \label{fig:72022}
\end{figure}

选择好路径后，点击\verb|Choose|确认，回到路径输入完成的界面（见\autoref{fig:72023}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72023.png}
    \caption{回到路径输入完成的界面}
    \label{fig:72023}
\end{figure}

点击\verb|Submit|提交请求，显示操作成功（见\autoref{fig:72024}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72024.png}
    \caption{提交请求}
    \label{fig:72024}
\end{figure}

打开生成的报告查看（见\autoref{fig:72025}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72025.png}
    \caption{生成的报告}
    \label{fig:72025}
\end{figure}

\subsubsection{批量开户}

首先，点击\verb|Create users from xls file|进入批量开户界面（见\autoref{fig:72031}）。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.3\textwidth]{72031.png}
    \caption{进入批量开户界面}
    \label{fig:72031}
\end{figure}

点击\verb|Browse|打开选择文件界面（见\autoref{fig:72032}）。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.3\textwidth]{72032.png}
    \caption{打开选择文件界面}
    \label{fig:72032}
\end{figure}

选择好文件后，点击\verb|Choose|确认，回到路径输入完成的界面（见\autoref{fig:72033}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72033.png}
    \caption{回到路径输入完成的界面}
    \label{fig:72033}
\end{figure}

点击\verb|Submit|提交请求，显示操作成功（见\autoref{fig:72034}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72034.png}
    \caption{提交请求}
    \label{fig:72034}
\end{figure}

打开开户的结果查看（见\autoref{fig:72035}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72035.png}
    \caption{开户的结果}
    \label{fig:72035}
\end{figure}

\subsubsection{导出用户信息}

首先，点击\verb|Query and export|进入导出用户信息界面（见\autoref{fig:72041}）。

\begin{figure}[ht]
    \centering
    \includegraphics[width=.3\textwidth]{72041.png}
    \caption{进入导出用户信息界面}
    \label{fig:72041}
\end{figure}

点击\verb|Browse|打开选择路径界面（见\autoref{fig:72042}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72042.png}
    \caption{打开选择路径界面}
    \label{fig:72042}
\end{figure}

选择好路径后，点击\verb|Choose|确认，回到路径输入完成的界面（见\autoref{fig:72043}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72043.png}
    \caption{回到路径输入完成的界面}
    \label{fig:72043}
\end{figure}

点击\verb|Submit|提交请求，显示操作成功（见\autoref{fig:72044}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72044.png}
    \caption{提交请求}
    \label{fig:72044}
\end{figure}

打开用户信息表查看（见\autoref{fig:72045}）。

\begin{figure}[!h]
    \centering
    \includegraphics[width=.3\textwidth]{72045.png}
    \caption{用户信息表}
    \label{fig:72045}
\end{figure}



% \mc{L1}{TBD}
% \subsection{操作规范}
% 本节将对所有菜单选项的操作规范进行说明，并测试各选项的功能。

% 当您处于主菜单界面时，会收到``请选择操作：''的提示（如\fref{fig:runCode}所示）。此时，输入菜单选项前的数字，即可选择对应选项。各菜单选项的详细信息如下：

% \subsubsection{显示当前卫星列表}
% 当您在主菜单界面输入``1''并按下回车键后，进入本选项。

% 进入本选项后，无需输入其他内容。管理系统会将当前所有卫星的信息（包括：COSPARID、名称、轨道半径和可用状态）输出在屏幕上，如\fref{fig:60101}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60101.png}
%     \caption{选择显示当前卫星列表}
%     \label{fig:60101}
% \end{figure}

% \subsubsection{注册新卫星}
% 当您在主菜单界面输入``2''并按下回车键后，进入本选项。

% 进入本选项后，需要根据提示，依次输入卫星的COSPARID、名称和轨道半径（可用状态默认为激活。如需封锁，可以在注册完成后选择选项5进行封锁）。其中，合法的卫星COSPARID必须是由两位国家编码（字母）加四位数字组成的长度为6的字符串，名称无限制，轨道半径必须是实数且不小于1.2。注册新卫星时，还需要保证卫星的COSPARID唯一，且卫星的轨道半径满足与其他的卫星轨道间隔不小于0.2。输入符合要求的卫星信息后，系统会将新卫星加入到卫星列表中，并显示注册卫星的信息，如\fref{fig:60102}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60102.png}
%     \caption{选择注册新卫星}
%     \label{fig:60102}
% \end{figure}

% \subsubsection{删除旧卫星}
% 当您在主菜单界面输入``3''并按下回车键后，进入本选项。

% 进入本选项后，需要根据提示，输入卫星的COSPARID。其中，合法的卫星COSPARID必须是由两位国家编码（字母）加四位数字组成的长度为6的字符串。删除卫星时，需要确认卫星列表中存在COSPARID与给定值相符的卫星。输入符合要求的卫星COSPARID后，系统会将相应卫星从卫星列表中删除，并显示被删除卫星的COSPARID，如\fref{fig:60103}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60103.png}
%     \caption{选择删除旧卫星}
%     \label{fig:60103}
% \end{figure}

% \subsubsection{激活卫星}
% 当您在主菜单界面输入``4''并按下回车键后，进入本选项。

% 进入本选项后，需要根据提示，输入卫星的COSPARID。其中，合法的卫星COSPARID必须是由两位国家编码（字母）加四位数字组成的长度为6的字符串。激活卫星时，需要确认卫星列表中存在COSPARID与给定值相符的卫星。输入符合要求的卫星COSPARID后，系统会将相应卫星的可用状态改为激活，并显示被激活卫星的信息，如\fref{fig:60104}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60104.png}
%     \caption{选择激活卫星}
%     \label{fig:60104}
% \end{figure}

% \subsubsection{封锁（失活）卫星}
% 当您在主菜单界面输入``5''并按下回车键后，进入本选项。

% 进入本选项后，需要根据提示，输入卫星的COSPARID。其中，合法的卫星COSPARID必须是由两位国家编码（字母）加四位数字组成的长度为6的字符串。封锁卫星时，需要确认卫星列表中存在COSPARID与给定值相符的卫星。输入符合要求的卫星COSPARID后，系统会将相应卫星的可用状态改为封锁，并显示被封锁卫星的信息，如\fref{fig:60105}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60105.png}
%     \caption{选择封锁（失活）卫星}
%     \label{fig:60105}
% \end{figure}

% \subsubsection{显示失活卫星列表}
% 当您在主菜单界面输入``6''并按下回车键后，进入本选项。

% 进入本选项后，无需输入其他内容。管理系统会将当前所有失活卫星的信息（包括：COSPARID、名称、轨道半径和可用状态）输出在屏幕上，如\fref{fig:60106}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60106.png}
%     \caption{选择显示失活卫星列表}
%     \label{fig:60106}
% \end{figure}

% \subsubsection{按名称模糊查找卫星}
% 当您在主菜单界面输入``7''并按下回车键后，进入本选项。

% 进入本选项后，需要根据提示，输入卫星的名称。管理系统会将当前所有名称中包含给定名称的卫星信息（包括：COSPARID、名称、轨道半径和可用状态）输出在屏幕上（给定名称为空时列出所有卫星的信息），如\fref{fig:60107}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60107.png}
%     \caption{选择按名称模糊查找卫星}
%     \label{fig:60107}
% \end{figure}

% \subsubsection{修改卫星信息}
% 当您在主菜单界面输入``8''并按下回车键后，进入本选项。

% 进入本选项后，需要根据提示，依次输入卫星的COSPARID、名称和轨道半径。其中，合法的卫星COSPARID必须是由两位国家编码（字母）加四位数字组成的长度为6的字符串，名称无限制，轨道半径必须是实数且不小于1.2。修改卫星信息时，还需要保证卫星列表中存在COSPARID与给定值相符的卫星，且卫星的轨道半径满足与其他的卫星轨道间隔不小于0.2。输入符合要求的卫星信息后，系统会将卫星的名称和轨道半径修改为给定值（默认卫星的COSPARID不会发生改变），并显示修改后的卫星信息，如\fref{fig:60108}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60108.png}
%     \caption{选择修改卫星信息}
%     \label{fig:60108}
% \end{figure}

% \subsubsection{退出}
% 当您在主菜单界面输入``9''并按下回车键后，进入本选项。

% 进入本选项后，无需输入其他内容。管理系统会在输出告别语后退出，如\fref{fig:60109}所示。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60109.png}
%     \caption{选择退出}
%     \label{fig:60109}
% \end{figure}

% \subsection{错误处理}
% 在本节中，仅对所有可能出现的错误的处理方式进行介绍，部分无错误的菜单选项在此不再赘述。

% \subsubsection{在主菜单输入不合法选项}
% 不合法的选项可能包括：输入内容多于一个部分（\fref{fig:60201A}）、输入的选项不是数字或整数（\fref{fig:60201B}）、输入的选项不属于菜单选项（\fref{fig:60201C}）。

% \begin{figure}[ht]
%     \centering
%     \subfloat[输入内容多于一个部分]{
%         \includegraphics[width=.3\textwidth]{60201A.png}
%         \label{fig:60201A}
%     }
%     \subfloat[输入的选项不是数字或整数]{
%         \includegraphics[width=.3\textwidth]{60201B.png}
%         \label{fig:60201B}
%     }
%     \subfloat[输入的选项不属于菜单选项]{
%         \includegraphics[width=.3\textwidth]{60201C.png}
%         \label{fig:60201C}
%     }
%     \caption{在主菜单输入不合法选项}
%     \label{fig:60201}
% \end{figure}

% 解决方案：输入欲选择的菜单选项前的数字。

% \subsubsection{注册新卫星时COSPARID不合法}
% \label{section:illegalID}
% 不合法的COSPARID可能包括：长度不等于6（\fref{fig:60202A}）、前两位不是国家代码（\fref{fig:60202B}）、后四位不是数字（\fref{fig:60202C}）、COSPARID不唯一（\fref{fig:60202D}）。

% \begin{figure}[ht]
%     \centering
%     \subfloat[长度不等于6]{
%         \includegraphics[width=.45\textwidth]{60202A.png}
%         \label{fig:60202A}
%     }
%     \subfloat[前两位不是国家代码]{
%         \includegraphics[width=.45\textwidth]{60202B.png}
%         \label{fig:60202B}
%     }\\
%     \subfloat[后四位不是数字]{
%         \includegraphics[width=.45\textwidth]{60202C.png}
%         \label{fig:60202C}
%     }
%     \subfloat[COSPARID不唯一]{
%         \includegraphics[width=.45\textwidth]{60202D.png}
%         \label{fig:60202D}
%     }
%     \caption{注册新卫星时COSPARID不合法}
%     \label{fig:60202}
% \end{figure}

% 解决方案：输入唯一的、由两位国家编码（字母）加四位数字组成的长度为6的COSPARID。

% \subsubsection{注册新卫星时轨道半径不合法}
% 不合法的COSPARID可能包括：轨道半径小于1.2（\fref{fig:60203A}）、与其他的卫星轨道间隔小于0.2（\fref{fig:60203B}）。

% \begin{figure}[ht]
%     \centering
%     \subfloat[轨道半径小于1.2]{
%         \includegraphics[width=.45\textwidth]{60203A.png}
%         \label{fig:60203A}
%     }
%     \subfloat[与其他的卫星轨道间隔小于0.2]{
%         \includegraphics[width=.45\textwidth]{60203B.png}
%         \label{fig:60203B}
%     }
%     \caption{注册新卫星时轨道半径不合法}
%     \label{fig:60203}
% \end{figure}

% 解决方案：输入不小于1.2的实数表示轨道半径，且满足与其他的卫星轨道间隔不小于0.2。

% \subsubsection{删除或激活或封锁卫星时COSPARID不合法}
% 不合法的COSPARID可能包括：长度不等于6、前两位不是国家代码、后四位不是数字。

% 解决方案：输入由两位国家编码（字母）加四位数字组成的长度为6的COSPARID。

% \subsubsection{删除或激活或封锁卫星时卫星不存在}
% 卫星不存在的原因为：输入的COSPARID有误（\fref{fig:60204}，以删除卫星时为例）。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60204.png}
%     \caption{删除卫星时卫星不存在}
%     \label{fig:60204}
% \end{figure}

% 解决方案：输入当前卫星列表中目标卫星对应的COSPARID。

% \subsubsection{修改卫星信息时COSPARID不合法}
% 不合法的COSPARID可能包括：长度不等于6、前两位不是国家代码、后四位不是数字。

% 解决方案：输入由两位国家编码（字母）加四位数字组成的长度为6的COSPARID。

% \subsubsection{修改卫星信息时卫星不存在}
% 卫星不存在的原因为：输入的COSPARID有误（\fref{fig:60205}）。

% \begin{figure}[ht]
%     \centering
%     \includegraphics[width=.9\textwidth]{60205.png}
%     \caption{修改卫星信息时卫星不存在}
%     \label{fig:60205}
% \end{figure}

% 解决方案：输入当前卫星列表中目标卫星对应的COSPARID。

% \subsubsection{修改卫星信息时轨道半径不合法}
% 不合法的COSPARID可能包括：轨道半径小于1.2（\fref{fig:60206A}）、与其他的卫星轨道间隔小于0.2（\fref{fig:60206B}）。

% \begin{figure}[ht]
%     \centering
%     \subfloat[轨道半径小于1.2]{
%         \includegraphics[width=.45\textwidth]{60206A.png}
%         \label{fig:60206A}
%     }
%     \subfloat[与其他的卫星轨道间隔小于0.2]{
%         \includegraphics[width=.45\textwidth]{60206B.png}
%         \label{fig:60206B}
%     }
%     \caption{修改卫星信息时轨道半径不合法}
%     \label{fig:60206}
% \end{figure}

% 解决方案：输入不小于1.2的实数表示轨道半径，且满足与其他的卫星轨道间隔不小于0.2。

\end{document} 